/**
 * 功能：16位与32位的启动混合代码
 *
 *创建时间：2022年8月31日
 *作者：李述铜
 *联系邮箱: 527676163@qq.com
 *相关信息：此工程为《从0写x86 Linux操作系统》的前置课程，用于帮助预先建立对32位x86体系结构的理解。整体代码量不到200行（不算注释）
 *课程请见：https://study.163.com/course/introduction.htm?courseId=1212765805&_trace_c_p_k2_=0bdf1e7edda543a8b9a0ad73b5100990
 */
	#include "os.h"

	// 声明本地以下符号是全局的，在其它源文件中可以访问
	.global _start

	// 指定以下的代码生成16位的机器指令，这样才能在启动时的实模式下运行
  	.code16

	// 以下是代码区
 	.text
_start:
	// x86使用段寄存器中的内容作为内存访问的基址，因此以下预先设置好
	mov $0, %ax				// 设置代码段
	mov %ax, %ds			// 设置数据段
	mov %ax, %es			// 设置数据段
	mov %ax, %ss			// 设置栈段
	mov %ax, %gs
	mov %ax, %fs
	mov $_start, %esp		// 设置栈的起始地址

	// 计算机上电启动后，只取512字节到0x7c00处，即相当于自己只有个头在内存，所以下面全部出来
	// 将自己的其余部分读取到0x7E00处，即0x7c00的后512字节
read_self_all:
	mov $_start_32, %bx	// 读取到的内存地址
	mov $0x2, %cx		// ch:磁道号，cl起始扇区号。0道0面2扇区
	mov $0x0240, %ax	// ah: 0x02读磁盘命令, al=0x40 64个扇区，多读一些, 32KB
	mov $0x80, %dx		// dh: 磁头号(0面)，dl驱动器号0x80(磁盘1)
	int $0x0013
	jc read_self_all	// 读取失败CF=1，则重复读取磁盘数据

	// 以下进入保护模式
	cli					//关闭中断
	lgdt gdt_desc		//加载gdt

	// 设置 cr0 寄存器的 pe=1
	mov $1, %eax		//将立即数 1 传送到 eax 寄存器中
	lmsw %ax			//lmsw 属于特权指令，只有在特权级为 0（通常是操作系统内核模式）时才能执行。PE 位会被置为 1，处理器便进入保护模式。

	// 刷新描述符高速缓存器内容，同时这使用的段选择子跳转到标号_start_32位置执行
	jmp $KERNEL_CODE_SEG, $_start_32

	// 这里不足 512 字节的话，会自动补齐 0x00 

	// 跳到引导标志, 由bios在上电后检查
	.org 0x1fe			// 引导标志
	.byte 0x55, 0xaa

	.code32
	.text
_start_32:						    //这里其实是从物理内存 0x7e00 开始的，因为前文正好是 512 个字节。0x7c00 + 512字节 = 0x7e00
    // 重新设置寄存器，刷新段选择子
	mov $KERNEL_DATA_SEG, %ax				
	mov %ax, %ds			// 设置数据段
	mov %ax, %es			// 设置数据段
	mov %ax, %ss			// 设置栈段
	mov %ax, %gs
	mov %ax, %fs
	jmp .			

// gdt描述符，由lgdt加载
gdt_desc: .word (256*8) - 1
	.long gdt_table